/**
 * @Description: 增强
 * @Author 卞鹏飞 <228443632@qq.com>
 * @create 07/08/24 9:44 PM
 */
import useLayoutStore from '@store/use-layout-store'
import { onBeforeUnmount, reactive, type UnwrapNestedRefs } from 'vue'
import { useRouter } from 'vue-router'
import {
  arrayToObj,
  hasOwn,
  isFunction,
  isPlainObject,
  isString,
  parseURL,
  stringPathJoin,
  uniq,
  uuid
} from 'sf-utils2'
import useAppStore from '@/store/use-app-store'
import useMenuStore from '@store/use-menu-store.ts'

// types
import type { TRouteRecordList, TRouter, TRouteLocationRaw } from '@root/types/router'
import type { RouteLocationNormalizedLoaded } from 'vue-router'
import profile from '@root/profile'
import type { TabItemUnion } from '@root/types/layout-store'

type TRouterEntry = UnwrapNestedRefs<{
  router: TRouter
  route: RouteLocationNormalizedLoaded
}>

let _router: TRouter

/**
 * 增强路由
 * @param router
 */
export function enhanceRouter(router: TRouter) {
  _router = (router || useRouter()) as TRouter

  const appStore = useAppStore()
  const layoutStore = useLayoutStore()
  const menuStore = useMenuStore()

  _router.__vars.refreshRoute = (route: TRouteLocationRaw, ...args: any[]) => {
    let key
    if (isString(route)) key = route
    else if (isPlainObject(route) && route.refresh === true) {
      const { fullPath } = router.resolve(route)
      key = fullPath
    }
    if (!key) return
    const layoutStore = useLayoutStore()
    layoutStore.reloadRoute(key, ...args) // 刷新路由}
  }

  /**
   * 更新tab
   */
  _router.updateTab = (...args) => {
    return layoutStore.updateTab(...args)
  }

  /**
   * 移除tab
   */
  _router.closeTab = (tab: Exclude<TabItemUnion, string>, options?: { autoJump: boolean }) => {
    return layoutStore.removeTab(tab, options)
  }

  /**
   * 刷新tab内容
   */
  _router.refreshTab = (...args) => {
    // @ts-expect-error
    return layoutStore.reloadRoute(...args)
  }

  /**
   * 设置路由是否缓存
   * @return {*[]}
   * TODO 卞
   */
  _router.__SET_CACHE_ASYNC_ROUTES = (routes: TRouteRecordList) => {
    console.time('处理异步路由生成缓存用时')
    routes ||= appStore.router.getRoutes() as TRouteRecordList
    // 路由
    const routeData = routes.reduce(
      (pre, cur) => {
        if (cur.__isDynamic) pre.dynamicRoutes.push(cur)
        else pre.noDynamicRoutes.push(cur)
        return pre
      },
      {
        noDynamicRoutes: [], // 非动态路由
        dynamicRoutes: [] // 动态路由
      }
    )
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const noDynamicRoutesCache = arrayToObj(routeData.noDynamicRoutes, 'path') // 非动态路由缓存

    // 菜单
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const menuData = menuStore._authMenuList.reduce(
      (pre, cur) => {
        if (cur.__isDynamic) pre.dynamicMenusList.push(cur)
        else pre.noDynamicMenusList.push(cur)
        return pre
      },
      {
        noDynamicMenusList: [], // 非动态菜单路由
        dynamicMenusList: [] // 动态菜单路由
      }
    )
  }

  /**
   * 解析获取组件整个 http地址，在window.open新开地址很有效
   * @return {*}
   * @example
   *   router.resolveRouteLocationHref({
   *      path: `/Login`,
   *      query: {
   *        redirect: encodeURIComponent(router.router?.fullPath)
   *      }
   *   })
   */
  _router.resolveRouteLocationHref = function () {
    // @ts-expect-error
    let { href } = router.resolve(...arguments) || {}
    const locationHref = window.location.href
    const prefixUrl = locationHref.split('#').filter(Boolean)?.at?.(0) || href
    href = stringPathJoin(prefixUrl, '#', href.replace(/^\/?#\/?/, ''))
    return href
  }

  /**
   * 解析获取组件整个 http地址，在window.open新开地址很有效
   * @return {*}
   * @example
   *   router.replaceRouteLocationHref({
   *      path: `/Login`,
   *      query: {
   *        redirect: encodeURIComponent(router.router?.fullPath)
   *      }
   *   })
   */
  _router.replaceRouteLocationHref = function () {
    // @ts-expect-error
    let href: string = router.resolveRouteLocationHref(...arguments)
    window.history.replaceState(null, null, href) // 设置一个状态值 来区分是否是点击浏览器的前进后退按钮触发的方法
  }

  /**
   * 关闭tab，之前确认函数
   * @param {() => boolean} cb 回调函数
   * @description
   * [onBeforeCloseRoute]
   * 里面接收参数是一个函数
   * 函数可以是promise、也可以是普通函数
   * 根据返回值，如果为true允许关闭tab，如果是false不允许关闭tab
   */
  _router.onBeforeCloseRoute = function (cb) {
    // beforeCloseRoute
    if (!isFunction(cb)) return
    const { router } = useAppStore()
    const layoutStore = useLayoutStore()
    const fullPath = router.currentRoute.fullPath
    const beforeCloseRoute = layoutStore.beforeCloseRoute
    beforeCloseRoute[fullPath] ||= []
    cb.key = uuid()
    beforeCloseRoute[fullPath] = uniq(beforeCloseRoute[fullPath].concat(cb as any))

    onBeforeUnmount(() => {
      const list = (beforeCloseRoute[fullPath] || []) as []
      const idx = list.find((v) => v === cb) // 移除
      if (~idx) {
        list.splice(idx, 1)
        if (!list.length) {
          delete beforeCloseRoute[fullPath]
        }
      }
    })
  }

  /**
   * 鉴权，判断是否有权限跳转
   * @param {string} path 全路径，包括地址参数
   * @private
   */
  _router.__AUTH_ROUTE_NEXT_TO__ = (path) => {
    // 判断是否有权限访问页面
    const urlParams = parseURL(path) // 地址参数
    const pathName = path?.split?.('?')?.at(0) || path
    /**
     * 判断是否含有params
     * @param {object} mustParams 必须要携带的参数对象
     */
    const hasParams = (mustParams) => {
      if (!Object.keys(mustParams).length) return true
      return Object.entries(mustParams).every(([key, value]) => {
        if (value == 'xxx') {
          // 表示xxx必须携带参数
          return hasOwn(urlParams, key)
        }
        // 如果值相同
        return value == urlParams[key]
      })
    }
    const targetMenu = menuStore._authMenuList.find((v) => v.exp?.test?.(pathName) && hasParams(v.params))
    // 如果没有权限，统一跳转到401 无权限访 问页面
    return {
      hasPermission: !!targetMenu,
      targetMenu
    }
  }
}

// 路由增强
export function useRouterPlus(): TRouterEntry {
  if (!profile.IS_PRO) {
    window.$debugRouter = _router // 全局变量，方便调试
  }

  return reactive({
    router: _router as TRouter,
    route: _router.currentRoute as unknown as RouteLocationNormalizedLoaded
  })
}

export default useRouterPlus
